package com.codeckz.library_face.controller

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Matrix
import android.graphics.Rect
import android.os.Build
import android.util.Size
import android.view.TextureView
import androidx.annotation.RequiresApi
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import com.codeckz.library_face.utils.*
import kotlinx.coroutines.*
import java.util.concurrent.Executors
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min


/**
 *@packageName com.codeckz.library_face.conttoller
 *@author kzcai
 *@date 2022/1/7
 */
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class CameraxController(private val context: Context, private val lifecycleOwner: LifecycleOwner) :
    ICameraController(), CoroutineScope by MainScope() {

    companion object {
        private const val RATIO_4_3_VALUE = 4.0 / 3.0
        private const val RATIO_16_9_VALUE = 16.0 / 9.0
    }

    private val cameraExecutor = Executors.newSingleThreadExecutor()

    private var previewView: PreviewView? = null

    private var camera: Camera? = null





    override fun setPreviewView(view: PreviewView) {
        previewView = view
    }

    override fun setPreviewView(view: TextureView) {

    }


    override fun startCamera() {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
        cameraProviderFuture.addListener({
            val cameraProvider = cameraProviderFuture.get()//获取相机信息
            val preview = Preview.Builder()
                .build().also {
                    it.setSurfaceProvider(previewView?.surfaceProvider)
                }

            try {
                cameraProvider.unbindAll()
                camera = cameraProvider.bindToLifecycle(
                    lifecycleOwner,
                    if (cameraFacing == ICameraController.FRONT_CAMERA) CameraSelector.DEFAULT_FRONT_CAMERA else CameraSelector.DEFAULT_BACK_CAMERA,
                    preview, imageAnalysisConfig
                )

                val zoomState = camera?.cameraInfo?.zoomState!!
                val zoomRatio = zoomState.value?.zoomRatio!!
                if (cameraFacing == ICameraController.FRONT_CAMERA) {
                    camera!!.cameraControl.setZoomRatio(0.4f)
                } else {
                    camera!!.cameraControl.setZoomRatio(0.4f)
                }

            } catch (e: Exception) {
                e.printStackTrace()
            }
        }, ContextCompat.getMainExecutor(context))
    }

    override fun switchCamera() {
        if (cameraFacing == BACK_CAMERA) {
            switchCamera(FRONT_CAMERA)
        } else {
            switchCamera(BACK_CAMERA)
        }
    }

    override fun switchCamera(cameraFacing: Int) {
        this.cameraFacing = cameraFacing
        startCamera()
    }


    private fun aspectRatio(width: Int, height: Int): Int {
        val previewRatio = max(width, height).toDouble() / min(width, height)
        if (abs(previewRatio - RATIO_4_3_VALUE) <= abs(previewRatio - RATIO_16_9_VALUE)) {
            return AspectRatio.RATIO_4_3
        }
        return AspectRatio.RATIO_16_9
    }

    private var imageAnalysis: ImageAnalysis? = null
    private val imageAnalysisConfig: ImageAnalysis
        @RequiresApi(Build.VERSION_CODES.LOLLIPOP) @SuppressLint("UnsafeOptInUsageError")
        get() {
            if (imageAnalysis == null) {
                val screenAspectRatio = aspectRatio(
                    ScreenUtils.getScreenWidth(context),
                    ScreenUtils.getScreenHeight(context)
                )
                val rotation = previewView?.display?.rotation
                imageAnalysis = ImageAnalysis.Builder()
//                    .setTargetAspectRatio(screenAspectRatio)
                    .setTargetResolution(Size(1920, 1080))
                    .setImageQueueDepth(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                    .setTargetRotation(rotation!!)
                    .build()
                imageAnalysis!!.setAnalyzer(cameraExecutor) { imageProxy ->
                    launch (Dispatchers.IO){
                        if (circleRect == null) {
                            return@launch
                        }

                        if (imageProxy.planes.isNotEmpty()) {

                            val bitmap = imageProxy.toBitmap()
                            val matrix = Matrix()
                            matrix.setRotate(
                                imageProxy.imageInfo.rotationDegrees.toFloat()
                            )

                            val offset =
                                if (cameraFacing == ICameraController.FRONT_CAMERA) 20 else 0
//                        val newBM = bitmap?.fitScreen(
//                            context,
//                            imageProxy.imageInfo.rotationDegrees.toFloat()
//                        )
                            val newBM = withContext(Dispatchers.Main){
                                previewView!!.bitmap
                            }
//                            val cutBm = newBM?.cutRect(context, circleRect!!, offset)
                            val cutBm = newBM?.addBitmap(context,circleRect!!)

                            if (cutBm != null) {
                                if (filterFace == null) {
                                    filterFace = AndroidFilterFace()
                                }
                                val result = filterFace!!.checkFace(
                                    cutBm,
                                    circleRect!!.bitmapCheckRect(
                                        context,
                                        cutBm,
                                        offset
                                    )
                                )
                                val data = newBM.toByteArray(70)
                                onImageAnalysisListener?.invoke(data, result)
                                bitmap?.recycle()
                                newBM.recycle()
                                cutBm.recycle()
                            }
                        }
                        imageProxy.close()
                    }
                }

            }
            return imageAnalysis!!
        }


    override fun release() {
        onImageAnalysisListener = null
    }
}